
/*
 * phc -- the open source PHP compiler
 * See doc/license/README.license for licensing information
 *
 * Generate C code
 *
 * We define a virtual class "Pattern" which corresponds to a particular kind
 * of statement that we can generate code for. We inherit from Pattern to 
 * define the various statements we can translate, create instances of each
 * of these classes, and then for every statement in the input, cycle through
 * all the patterns to find one that matches, and then call "generate" on that
 * pattern.
 */

// TODO Variable_variables cannot be used to access superglobals. See warning
// in http://php.net/manual/en/language.variables.superglobals.php
// TODO: that is not true - add a test case.

// TODO:
//		magic methods are:
//			__construct
//			__destruct
//			__get
//			__set
//			__unset
//			__isset
//			__call
//			__toString
//			__serialize
//			__unserialize
//
//	So that means casts are pure.

#include <fstream>
#include <boost/format.hpp>

#include "lib/List.h"
#include "lib/Set.h"
#include "lib/Map.h"
#include "lib/escape.h"
#include "lib/demangle.h"
#include "process_ir/General.h"
#include "process_ir/XML_unparser.h"
#include "codegen/Generate_C_annotations.h"
#include "process_mir/MIR_to_AST.h"
#include "pass_manager/Pass_manager.h"

#include "Generate_C.h"
#include "parsing/MICG_parser.h"
#include "embed/embed.h"

using namespace boost;
using namespace MIR;
using namespace std;

/**
   just pass through this to a macro MyUnset
*/
class Pattern_Unset : public Pattern
{
public:
	bool match(Statement* that)
	{
	  //Name_with_default* var

	  //target = new Wildcard<Target>;
	  //var = new Wildcard<Variable_variable>;
	  //	  rv = new Wildcard<Rvalue_list>;

	  unset = new Wildcard<Unset>	     ;
	  bool ret= that->match(unset);
	  if (ret)
	    {
	      return true;
	    }
	  else
	    {
	      	cerr << "Not yet supported in code generation: " << "test" << endl;
		(new MIR_unparser (cerr, true))->unparse (that);
		cerr << endl;
		xml_unparse (that, cerr);
		return false;
	    }

	}


  /*  static void generate_code_new(Generate_C* gen)
  {
    
    }*/

  void generate_code(Generate_C* gen)
  {


    VARIABLE_NAME* var_name = dynamic_cast <VARIABLE_NAME*> (unset->value->variable_name);
    
    if (var_name != NULL)
      {
	if (unset->value->array_indices->size() == 0)
	  {
	    if (var_name->attrs->is_true ("phc.codegen.st_entry_not_required"))
	      {
		string name = get_non_st_name (var_name);
		buf
		  <<		"zval_ptr_dtor (" << name << ");\n";
		  << "}\n";
	      }
	    else
	      {
		String* name = var_name->value;
		buf
		  << "unset_var ("
		  <<		get_scope (LOCAL) << ", "
		  <<		"\"" << *name << "\", "
		  <<		name->length() + 1
		  // no get_hash version
		  <<		");\n";
	      }
	  }
	else 
	  {
	    buf
	      << "do\n"
	      << "{\n"
	      ;
	    
	    buf
	      << get_st_entry (LOCAL, "u_array", var_name)
	      << "sep_copy_on_write (u_array);\n"
	      << "zval* array = *u_array;\n"
	      ;
	    
	    Rvalue_list* indices = unset->value->array_indices->clone ();
	    Rvalue* back_index = indices->back ();
	    indices->pop_back ();
	    
	    // Foreach index, read the array, but do not update it in place.
	    foreach (Rvalue* index, *indices)
	      {
		buf
		  << "if (Z_TYPE_P (array) == IS_ARRAY)\n"
		  << "{\n"
		  <<		read_rvalue ("index", index)
		  // This uses check_array_index type because PHP behaves
		  // differently for the inner index check than the outer one.
		  <<	"	if (!check_array_index_type (index TSRMLS_CC))\n"
		  <<	"	{\n"
		  <<	"		array = EG(uninitialized_zval_ptr);\n"
		  <<	"	}\n"
		  <<	"	else\n"
		  << "		read_array (&array, array, index TSRMLS_CC);\n"
		  << "}\n"
		  ;
	      }
	    buf
	      << read_rvalue ("index", back_index)
	      << "if (Z_TYPE_P (array) == IS_ARRAY)\n"
	      <<	"	if (!check_unset_index_type (index TSRMLS_CC)) break;\n"
	      
	      << "unset_array ("
	      <<    "&array, "
	      <<    "index "
	      <<		" TSRMLS_CC);\n"
	      ;
	    
	    buf
	      << "}\n"
	      << "while (0);\n"
	      ;
	  }
      }
    else
      {
	
	
	Variable_variable* var_name = 
	  dynamic_cast <Variable_variable*> (unset->value->variable_name);
	
	if (var_name)
	  {
	    String * pString=  var_name->variable_name->value;
	    buf << "MyUnset(";
	    buf << *pString;
	    buf << ");\n";
	
	    //  String * pString=  var->value->value;
	    //<< *pString << 
	    // buf << "MyUnset();\n";
	  }
      }
  }
protected:
  //MIR::Name_with_default*
  //	Wildcard<Name_with_default>* variable_name;

  //Wildcard<Target>* target;
  //  Wildcard<Variable_variable> * var;
  //  Wildcard<Rvalue_list> * rv;
  Wildcard<Unset>* unset;

};

Pattern * createPattern_unset()
{
  return new Pattern_Unset();
}



/*
<MIR:Unset>
	<attrs />
	<MIR:VARIABLE_NAME>
		<attrs>
			<attr key="phc.filename"><string>./Parser.php</string></attr>
			<attr key="phc.line_number"><integer>159</integer></attr>
		</attrs>
		<value>this</value>
	</MIR:VARIABLE_NAME>
	<MIR:Variable_variable>
		<attrs>
			<attr key="phc.filename"><string>./Parser.php</string></attr>
			<attr key="phc.line_number"><integer>159</integer></attr>
		</attrs>
		<MIR:VARIABLE_NAME>
			<attrs>
				<attr key="phc.filename"><string>./Parser.php</string></attr>
				<attr key="phc.line_number"><integer>159</integer></attr>
			</attrs>
			<value>name</value>
		</MIR:VARIABLE_NAME>
	</MIR:Variable_variable>
	<MIR:Rvalue_list>
	</MIR:Rvalue_list>
</MIR:Unset>
*/

/*
 * unset ($x); (1)
 * or
 * unset ($x[$i]); (2) (corresponds to ZEND_UNSET_DIM)
 * or
 * unset ($x[$i][$j]); (3)
 *
 * Semantics:
 *
 *	(1) Remove $x from the symbol-table
 *	(2) If $x is a string, Error.
 *	    If $x is another scalar, do nothing
 *	    If $x is an array, remove entry $i
 *	(3) Different rules for > 1D indexing.
 *		 If the index is invalid, return NULL from that portion of indexing.
 *		 Then initialize it to an array. Then proceed.
 *
 * Allowed index types:
 *		double
 *		long
 *		bool
 *		resource (convert to long)
 *		string
 *		NULL (as "")
 *
 *	Anything else results in "Illegal offset type"
 *
 */
/*
class Pattern_unset : public Pattern
{
	bool match(Statement* that)
	{
		unset = new Wildcard<Unset>;
		return that->match(unset);
	}

	void generate_code(Generate_C* gen)
	{
		else
		{
			// Variable variable
			///phc_unsupported (unset->value, "unset
			//variable variable");
		  

		}
	}

protected:
	Wildcard<Unset>* unset;
};
*/
